<?php
/**
 * #Celebrate Shieh
 *             ,%%%%%%%%,
 *           ,%%/\%%%%/\%%
 *          ,%%%\c "" J/%%%
 * %.       %%%%/ o  o \%%%
 * `%%.     %%%%    _  |%%%
 *  `%%     `%%%%(__Y__)%%'
 *  //       ;%%%%`\-/%%%'
 * ((       /  `%%%%%%%'
 *  \\    .'          |
 *   \\  /       \  | |
 *    \\/         ) | |
 *     \         /_ | |__
 *     (___________))))))) 攻城湿
 *
 *        _       _
 * __   _(_)_   _(_) __ _ _ __
 * \ \ / / \ \ / / |/ _` |'_ \
 *  \ V /| |\ V /| | (_| | | | |
 *   \_/ |_| \_/ |_|\__,_|_| |_|
 */

namespace App\Services;

use App\Models\System\Config;
use App\Models\System\File;
use App\Models\System\Format;
use App\Models\System\Info;
use App\Models\System\Primary;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;

class UploadService
{

    use Service;

    /**
     * 上传文件
     *
     * @param Request $request
     *
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
     */
    public function PicStoreService( $request )
    {
        DB::beginTransaction();//开启事务
        $user     = $request->input('user');
        $category = $request->input('category');
        try {
            $time  = Carbon::now();
            $error = $this->getErrorCode();
            if ( ! Auth::id() ) throw new \Exception($error::message(101));
            $disk = $this->getStorage();
            if ( ! $request->hasFile($request->input('get', 'file')) ) throw new \Exception($error::message(1005));
            $format      = Format::FindModel([ 'picture_cate', 'picture_size', 'picture' ]);//查询文件类型大小配置
            $name        = strtolower($format->picture);//转小写
            $allow_types = explode(',', $name);//类型
            $picture     = $request->file($request->input('get', 'file'));
            $extension   = strtolower($picture->getClientOriginalExtension());//上传文件类型转小写
            if ( ! in_array($extension, $allow_types) ) throw new \Exception('类型错误');//如果上传文件类型跟允许上传类型不匹配
            $size = size($format->picture_cate, $format->picture_size);//计算允许上传文件大小
            if ( $picture->getSize() > $size ) throw new \Exception('图片不能大于' . maxsize($size));
            $fileName    = $picture->getClientOriginalName();//获取文件名
            $newFileName = md5(uniqid(microtime(TRUE), TRUE)) . '.' . pathinfo($fileName, PATHINFO_EXTENSION);
            $savePath    = $user . '/' . Auth::id() . '/' . $category . '/' . $extension;//路径
            $picture->storePubliclyAs($savePath, $newFileName, [ 'disk' => 'public' ]);//移动到指定路径
            $url                  = $disk->url($savePath . '/' . $newFileName);//获取文件url
            $file_url             = $disk->fileUrl($savePath . '/' . $newFileName);//获取文件详细地址
            $data[ 'ext' ]        = $extension;
            $data[ 'uid' ]        = Auth::id();
            $data[ 'category' ]   = $category;
            $data[ 'name' ]       = $newFileName;
            $data[ 'filename' ]   = $fileName;
            $data[ 'url' ]        = $url;
            $data[ 'file_url' ]   = $file_url;
            $data[ 'user' ]       = $user;
            $data[ 'size' ]       = maxsize($picture->getSize());//计算文件大小
            $data[ 'created_at' ] = $time;
            $data[ 'updated_at' ] = $time;
            $data[ 'is_show' ]    = $request->input('is_show', 1);
            if ( $request->filled('user_id') ) $data[ 'user_id' ] = $request->input('user_id', 0);
            else $data[ 'user_id' ] = Auth::id();
            $gif = TRUE;
            switch ( $extension ) {
                case 'gif':
                    $gif   = config('page.gif');
                    $files = $file_url;
                    if ( $gif == 'true' ) {
                        $savePath = 'primary/' . Auth::id() . '/' . $category . '/' . $extension;//路径
                        $primary  = Primary::CreateModel($data);//原图
                        $percent  = 1;  #原图压缩，不缩放
                        $files    = storage_path('app/public/' . $savePath . '/' . $newFileName);
                        $disk->makeDirectory($savePath);//创建文件夹
                        $this->getPictures($file_url, $percent)->compressImg($files);//压缩图片
                    }
                    $data[ 'file_url' ] = $files;
                    $data[ 'url' ]      = $disk->url($savePath . '/' . $newFileName);
                    $data[ 'size' ]     = maxsize($disk->size($savePath . '/' . $newFileName));
                    break;
                default:
                    $savePath = 'primary/' . Auth::id() . '/' . $category . '/' . $extension;//路径
                    $primary  = Primary::CreateModel($data);//原图
                    $percent  = 1;  #原图压缩，不缩放
                    $files    = storage_path('app/public/' . $savePath . '/' . $newFileName);
                    $disk->makeDirectory($savePath);//创建文件夹
                    $this->getPictures($file_url, $percent)->compressImg($files);//压缩图片
                    $data[ 'file_url' ] = $files;
                    $data[ 'url' ]      = $disk->url($savePath . '/' . $newFileName);
                    $data[ 'size' ]     = maxsize($disk->size($savePath . '/' . $newFileName));
                    break;
            }
            $data2 = [];
            switch ( $category ) {
                case 'avatar'://头像
                    $where             = function ( $query ) use ( $category, $user ) {
                        $query->where('uid', Auth::id());
                        $query->where('category', $category);
                        $query->where('user', $user);
                    };
                    $info              = File::GetModel($where, [ 'id' ]);//查询之前得头像
                    $data1[ 'avatar' ] = File::InsertGetIdModel($data);
                    if ( $gif == 'true' ) $primary->file_id = $data1[ 'avatar' ];
                    $data1[ 'updated_at' ] = $time;
                    Info::UpdateModel(Auth::id(), $data1);
                    foreach ( $info as $value ) if ( ! empty($value) ) File::UpdateModel([ 'id' => $value->id ], [
                        'status'     => 2,
                        'deleted_at' => Carbon::now()->toDateTimeString(),
                    ]);//之前得头像都软删除
                    break;
                case 'config'://系统配置
                    $where            = function ( $query ) use ( $category, $user ) {
                        $query->where('uid', Auth::id());
                        $query->where('category', $category);
                        $query->where('user', $user);
                    };
                    $info             = File::GetModel($where, [ 'id' ]);//查询之前得系统图标
                    $data1[ 'image' ] = File::InsertGetIdModel($data);
                    if ( $gif == 'true' ) $primary->file_id = $data1[ 'image' ];
                    $data1[ 'updated_at' ] = $time;
                    Config::UpdateModel([ 'status' => 1 ], $data1);//更新系统图标
                    foreach ( $info as $value ) if ( ! empty($value) ) File::UpdateModel([ 'id' => $value->id ], [
                        'status'     => 2,
                        'deleted_at' => Carbon::now()->toDateTimeString(),
                    ]);//之前得系统图标都软删除
                    break;
                case 'blogConfig'://系统配置
                    $where            = function ( $query ) use ( $category, $user ) {
                        $query->where('uid', Auth::id());
                        $query->where('category', $category);
                        $query->where('user', $user);
                    };
                    $info             = File::GetModel($where, [ 'id' ]);//查询之前得系统图标
                    $data1[ 'image' ] = File::InsertGetIdModel($data);
                    if ( $gif == 'true' ) $primary->file_id = $data1[ 'image' ];
                    $data1[ 'updated_at' ] = $time;
                    Config::UpdateModel([ 'status' => 2 ], $data1);//更新系统图标
                    foreach ( $info as $value ) if ( ! empty($value) ) File::UpdateModel([ 'id' => $value->id ], [
                        'status'     => 2,
                        'deleted_at' => Carbon::now()->toDateTimeString(),
                    ]);//之前得系统图标都软删除
                    break;
                case 'email'://邮件
                    $file_id = File::InsertGetIdModel($data);
                    if ( $gif == 'true' ) $primary->file_id = $file_id;
                    $data2[ 'src' ]   = $url;
                    $data2[ 'title' ] = $newFileName;
                    break;
                case 'chat'://聊天
                    $file_id = File::InsertGetIdModel($data);
                    if ( $gif == 'true' ) $primary->file_id = $file_id;
                    $data2[ 'src' ]   = $url;
                    $data2[ 'title' ] = $newFileName;
                    break;
                default:
                    $data2[ 'id' ] = File::InsertGetIdModel($data);
                    if ( $gif == 'true' ) $primary->file_id = $data2[ 'id' ];
                    break;
            }
            if ( $gif == 'true' ) $primary->save();
        } catch ( \Exception $exception ) {
            errorStorage('upload', $exception->getTraceAsString(), $exception->getMessage(), 'error', '上传图片');
            DB::rollBack();//事务回滚
            switch ( $request->input('post') ) {
                case 'ajax'://ajax返回
                    return data(NULL, $this->getErrorCode()::message(1000), 1000);
                    break;
                case 'markdown'://markdown返回
                    return response()->json([
                        'url'     => '',
                        'success' => 0,
                        'message' => $this->getErrorCode()::message(1000),
                    ]);
                default:
                    return back()->with('error', $this->getErrorCode()::message(1000));
                    break;
            }
        }
        DB::commit();//提交事务
        $data = '"文件名" : ' . $data[ 'name' ] . '"后缀名" : ' . $data[ 'ext' ] . '"本地文件地址" : ' . $data[ 'file_url' ] . '"文件大小" : ' . $data[ 'size' ];
        errorStorage('upload', $data, '上传图片成功', 'success', '上传图片');
        switch ( $request->input('post') ) {
            case 'ajax':
                return data($data2, $error::message(202), 0);
                break;
            case 'markdown':
                return response()->json([
                    'url'     => $url,
                    'success' => 1,
                    'message' => '上传成功',
                ]);
            case 'link':
                return data($data2, $error::message(202), 200);
                break;
            default:
                return back()->with('success', $error::message(202));
                break;
        }
    }


    /**
     * 分片上传
     *
     * @param Request $request
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function PostFileImageService( $request )
    {
        @set_time_limit(0);
        $name     = $request->input('name');
        $user     = $request->input('user');
        $category = $request->input('category');
        $ext      = substr($name, strrpos($name, '.') + 1);//获取后缀名
        $type     = $this->TypeService(strtolower($ext), $request->input('status'));//根据上传后缀名来判断类型和大小
        $error    = $this->getErrorCode();
        if ( ! $type[ 'TRUE' ] ) return data(NULL, $error::message(1004), 1000);
        $size = size($type[ 'file_cate' ], $type[ 'file_size' ]);//根据类型和大小获取 值
        if ( $request->input('size') > $size ) return data(NULL, $type[ 'name' ] . '不能大于' . maxsize($size), 1000);
        $targetDir        = storage_path() . '/' . 'app/public/file';
        $uploadDir        = storage_path() . '/' . 'app/public';
        $cleanupTargetDir = TRUE;
        $maxFileAge       = 5 * 3600;
        if ( isset($name) ) $fileName = $name;
        else $fileName = uniqid("file_");
        $savePath   = $user . '/' . Auth::id() . '/' . $category . '/' . strtolower($ext);
        $filePath   = $targetDir . '/' . $savePath . '/' . $fileName;
        $uploadPath = $uploadDir . '/' . $savePath . '/';
        $disk       = $this->getStorage();
        $disk->makeDirectory('file');
        $disk->makeDirectory('file/' . $savePath);
        $disk->makeDirectory($savePath);
        $chunk  = intval($request->input('chunk', 0));//分片
        $chunks = intval($request->input('chunks', 1));
        if ( $cleanupTargetDir ) {
            if ( ! is_dir($targetDir . '/' . $savePath) || ! $dir = opendir($targetDir . '/' . $savePath) ) return message($error::message(1006));
            while ( ( $file = readdir($dir) ) !== FALSE ) {
                $tmpfilePath = $targetDir . '/' . $savePath . '/' . $file;
                if ( $tmpfilePath == "{$filePath}_{$chunk}.part" || $tmpfilePath == "{$filePath}_{$chunk}.parttmp" ) continue;
                if ( preg_match('/\.(part|parttmp)$/', $file) && ( @filemtime($tmpfilePath) < time() - $maxFileAge ) ) @unlink($tmpfilePath);
            }
            closedir($dir);
        }
        if ( ! $out = @fopen("{$filePath}_{$chunk}.parttmp", "wb") ) return message($error::message(1007));
        if ( ! empty($_FILES) ) {
            if ( $_FILES[ "file" ][ "error" ] || ! is_uploaded_file($_FILES[ "file" ][ "tmp_name" ]) ) return message($error::message(1008));
            if ( ! $in = @fopen($_FILES[ "file" ][ "tmp_name" ], "rb") ) return message($error::message(1009));
        }
        else if ( ! $in = @fopen("php://input", "rb") ) return message($error::message(1010));
        while ( $buff = fread($in, 4096) ) fwrite($out, $buff);
        @fclose($out);
        @fclose($in);
        rename("{$filePath}_{$chunk}.parttmp", "{$filePath}_{$chunk}.part");
        $done = TRUE;
        for ( $index = 0; $index < $chunks; $index++ ) if ( ! file_exists("{$filePath}_{$index}.part") ) {
            $done = FALSE;
            break;
        }
        if ( $done ) {
            if ( ! $out = @fopen($filePath, "wb") ) return message($error::message(1011));
            if ( flock($out, LOCK_EX) ) {
                for ( $index = 0; $index < $chunks; $index++ ) {
                    if ( ! $in = @fopen("{$filePath}_{$index}.part", "rb") ) break;
                    while ( $buff = fread($in, 4096) ) fwrite($out, $buff);
                    @fclose($in);
                    @unlink("{$filePath}_{$index}.part");
                }
                flock($out, LOCK_UN);
            }
            @fclose($out);
            $newFileName          = md5(uniqid(microtime(TRUE), TRUE)) . '.' . pathinfo($fileName, PATHINFO_EXTENSION);
            $time                 = Carbon::now();
            $data[ 'ext' ]        = strtolower($ext);
            $data[ 'user' ]       = $user;
            $data[ 'uid' ]        = Auth::id();
            $data[ 'category' ]   = $category;
            $data[ 'filename' ]   = $fileName;
            $data[ 'name' ]       = $newFileName;
            $data[ 'created_at' ] = $time;
            $data[ 'updated_at' ] = $time;
            $data[ 'is_show' ]    = $request->input('is_show', 1);
            if ( $request->filled('user_id') ) $data[ 'user_id' ] = $request->input('user_id', 0);
            else $data[ 'user_id' ] = Auth::id();
            $result = DB::transaction(function () use ( $request, $type, $ext, $category, $disk, $filePath, $newFileName, $savePath, $uploadPath, $error, $data ) {
                switch ( $type[ 'name' ] ) {
                    case '图片':
                        $gif  = config('page . gif');
                        $true = TRUE;
                        if ( strtolower($ext) == 'gif' && $gif != 'TRUE' ) $true = FALSE;
                        if ( $true ) {
                            $disk->makeDirectory('primary/' . Auth::id() . '/' . $category . '/' . strtolower($ext));
                            $savePaths = 'primary/' . Auth::id() . '/' . $category . '/' . strtolower($ext);
                            $file_url  = storage_path('app/public/' . $savePaths . '/' . $newFileName); //可加存放路径
                            rename($filePath, $file_url);//移动文件
                            @unlink($filePath);
                            $url                = $disk->url($savePaths . '/' . $newFileName);//获取URL
                            $data[ 'url' ]      = $url;
                            $data[ 'file_url' ] = $file_url;
                            $data[ 'size' ]     = maxsize($request->input('size'));
                            $primary            = Primary::CreateModel($data);//原图
                            $files              = storage_path('app/public/' . $savePath . '/' . $newFileName);
                            $disk->makeDirectory($savePath);//创建文件夹
                            $this->getPictures($file_url)->compressImg($files);//压缩图片
                            $data[ 'file_url' ] = $files;
                            $data[ 'url' ]      = $disk->url($savePath . '/' . $newFileName);
                            $data[ 'size' ]     = maxsize($disk->size($savePath . '/' . $newFileName));
                            $file[ 'id' ]       = File::InsertGetIdModel($data);//存放缩略图
                            $primary->file_id   = $file[ 'id' ];
                            $primary->save();
                        }
                        else {
                            rename($filePath, $uploadPath . '/' . $newFileName);//移动文件
                            @unlink($filePath);
                            $file_url           = $disk->fileUrl($savePath . '/' . $newFileName);
                            $url                = $disk->url($savePath . '/' . $newFileName);
                            $data[ 'url' ]      = $url;
                            $data[ 'file_url' ] = $file_url;
                            $data[ 'size' ]     = maxsize($request->input('size'));
                            $file[ 'id' ]       = File::InsertGetIdModel($data);
                        }
                        break;
                    default:
                        rename($filePath, $uploadPath . '/' . $newFileName);//移动文件
                        @unlink($filePath);
                        $file_url           = $disk->fileUrl($savePath . '/' . $newFileName);
                        $url                = $disk->url($savePath . '/' . $newFileName);
                        $data[ 'url' ]      = $url;
                        $data[ 'file_url' ] = $file_url;
                        $data[ 'size' ]     = maxsize($request->input('size'));
                        $file[ 'id' ]       = File::InsertGetIdModel($data);
                }
                $file[ 'name' ] = $newFileName;
                $data1          = '"文件名" : ' . $data[ 'name' ] . '"后缀名" : ' . $data[ 'ext' ] . '"本地文件地址" : ' . $data[ 'file_url' ] . '"文件大小" : ' . $data[ 'size' ];
                errorStorage('upload', $data1, '上传图片成功', 'success', '上传图片');
                return data($file, $error::message(202));
            });
            return $result;
        }
        return data();
    }

    /**
     * @param string $extension
     * @param int    $status
     *根据上传文件后缀名来判断上传类型和大小
     *
     * @return mixed
     */
    public function TypeService( string $extension, $status = 0 )
    {
        $format              = Format::FindModel([
            'picture', 'picture_cate', 'picture_size', 'file', 'file_cate', 'file_size', 'text', 'text_cate',
            'text_size',
        ]);
        $name                = strtolower($format->picture);
        $name1               = strtolower($format->file);
        $name2               = strtolower($format->text);
        $allow_types         = explode(',', $name);
        $allow_types1        = explode(',', $name1);
        $allow_types2        = explode(',', $name2);
        $data[ 'TRUE' ]      = FALSE;
        $data[ 'file_cate' ] = 0;
        $data[ 'file_size' ] = 0;
        $data[ 'name' ]      = '未知';
        switch ( $status ) {
            case 1:
                if ( in_array($extension, $allow_types) ) {
                    $data[ 'file_cate' ] = $format->picture_cate;
                    $data[ 'file_size' ] = $format->picture_size;
                    $data[ 'name' ]      = '图片';
                    $data[ 'TRUE' ]      = TRUE;
                }
                break;
            default:
                if ( in_array($extension, $allow_types) ) {
                    $data[ 'file_cate' ] = $format->picture_cate;
                    $data[ 'file_size' ] = $format->picture_size;
                    $data[ 'name' ]      = '图片';
                    $data[ 'TRUE' ]      = TRUE;
                }
                if ( in_array($extension, $allow_types1) ) {
                    $data[ 'file_cate' ] = $format->file_cate;
                    $data[ 'file_size' ] = $format->file_size;
                    $data[ 'name' ]      = '文件';
                    $data[ 'TRUE' ]      = TRUE;
                }
                if ( in_array($extension, $allow_types2) ) {
                    $data[ 'file_cate' ] = $format->text_cate;
                    $data[ 'file_size' ] = $format->text_size;
                    $data[ 'name' ]      = '文本';
                    $data[ 'TRUE' ]      = TRUE;
                }
                break;
        }
        return $data;
    }

}
